# -*- encoding: utf-8
# Protego Tutalum
# Avada Ke Covid-19
from ctypes import Union
from math import sqrt
from time import time
from copy import deepcopy
import random
import pygame
import sys
import os
import api.openapi as openapi


# Class Define

class Block:
    def __init__(self, strID, strBlocksShortName, isCanBeDigged, strDiggingType, iHardness, GiveAfterDigging, isCanMake,
                 HowToMake, strDigLevel, color):
        self.strID = strID
        self.strBlocksShortName = strBlocksShortName
        self.isCanBeDigged = isCanBeDigged
        self.strDigType = strDiggingType
        self.iHardness = iHardness
        self.GiveAfterDigging = GiveAfterDigging
        self.isCanMake = isCanMake
        self.HowToMake = HowToMake
        self.strDigLevel = strDigLevel
        self.color = color

    def to_dict(self):
        newdict = {"strID": self.strID, "strBlocksShortName": self.strBlocksShortName,
                   "isCanBeDigged": self.isCanBeDigged, "strDiggingType": self.strDigType, "iHardness": self.iHardness,
                   "GiveAfterDigging": self.GiveAfterDigging, "isCanMake": self.isCanMake, "HowToMake": self.HowToMake,
                   "strDigLevel": self.strDigLevel, "color": self.color}
        return newdict


class NormalItem:
    def __init__(self, strID, name, typ, dtyp, attack):
        self.isNormal = True
        self.strID = strID
        self.strMakeType = typ
        self.strDigType = dtyp
        self.strName = name
        self.iAttack = attack

    def to_dict(self):
        newdict = {"isNormal": True, "strID": self.strID, "strMakeType": self.strMakeType,
                   "strDigType": self.strDigType, "strName": self.strName, "iAttack": self.iAttack}
        return newdict


class InventoryThing:
    def __init__(self, typ, dat, cnt):
        self.strType = typ
        self.data = dat
        self.iCount = cnt

    def to_dict(self):
        newdict = {"strType": self.strType,
                   "iCount": self.iCount, "data": self.data.to_dict()}
        return newdict


class MobChicken:
    strShortName = "鸡"
    strID = "chicken"
    iLastJumpTick = 0
    iLastLeftTick = 0
    iLastRightTick = 0
    iHP = 4
    strLastMoveType = "n"  # n/l/r
    size = (1, 1)
    shape = [[2]]  # 0=不显示，1=显示，2=坐标位置
    summonon = ["grass_block"]
    ispausing = False

    def __init__(self, x, y, speed: Union[str, int] = "rand", likemoving="rand"):
        self.x = x
        self.y = y
        if speed == "rand":
            self.speed = random.randint(10, 15) / 10
        else:
            self.speed = speed / 10  # speed∈Z, 10<=speed<=15, speed值越小移动越快
        if likemoving == "rand":
            self.likemoving = True if random.randint(1, 2) == 1 else False
        else:
            self.likemoving = likemoving

    def left(self):
        if x % 16 == 0:  # Chunk Edge
            if LeftChunk[y][15].strID == "air" and iTick >= iLastLeftTick+iMovingWaitTime:
                x = x-1
                iLastLeftTick = iTick
                self.pause = True
                self.strLastMoveType = 'l'
        else:
            if MainChunk[y][x % 16-1].strID == "air" and iTick >= iLastLeftTick+iMovingWaitTime:
                x = x-1
                iLastLeftTick = iTick
                self.strLastMoveType = 'l'

    def right(self):
        if x % 16 == 15:  # Chunk Edge
            if [RightChunk[y][0].strID, RightChunk[y+1][0].strID] == ["air"]*2 and iTick >= iLastRightTick+iMovingWaitTime:
                x = x+1
                iLastRightTick = iTick
                self.pause = True
                self.strLastMoveType = 'r'
        else:
            if [MainChunk[y][x % 16+1].strID, MainChunk[y+1][x % 16+1].strID] == ["air"]*2 and iTick >= iLastRightTick+iMovingWaitTime:
                x = x+1
                iLastRightTick = iTick
                self.strLastMoveType = 'r'

    def jump(self):
        if MainChunk[self.y + 1][self.x].strID != "air":
            return
        if MainChunk[self.y - 1][self.x].strID == "air":
            return
        self.y = self.y + 1
        self.iLastJumpTick = iTick

    def fall(self):
        if MainChunk[self.y - 1][self.x].strID != "air":
            return
        if iTick <= iMovingWaitTime * 5 + self.iLastJumpTick:
            return
        self.y = self.y - 1

    def do_nothing(self):  # 空闲时走动
        if self.pause:
            return
        self.fall()
        if iTick % 10 == 0:
            if random.randint(1, (5 if self.likemoving else 10)) == 1:
                if self.strLastMoveType == "n":
                    if random.randint(1, 2) == 1:
                        self.jump()
                        self.left()
                    else:
                        self.jump()
                        self.right()
                elif self.strLastMoveType == "l":
                    if random.randint(1, 3) != 1:
                        self.jump()
                        self.left()
                    else:
                        self.jump()
                        self.right()
                else:
                    if random.randint(1, 3) != 1:
                        self.jump()
                        self.left()
                    else:
                        self.jump()
                        self.right()

    def to_dict(self):
        newdict = {"type": "chicken", "x": self.x, "y": self.y,
                   "speed": self.speed, "likemoving": self.likemoving}
        return newdict


class MobPig:
    strShortName = "猪"
    strID = "pig"
    iLastJumpTick = 0
    iLastLeftTick = 0
    iLastRightTick = 0
    iHP = 4
    strLastMoveType = "n"  # n/l/r
    size = (1, 1)
    shape = [[2]]  # 0=不显示，1=显示，2=坐标位置
    summonon = ["grass_block"]

    def __init__(self, x, y, speed="rand", likemoving="rand"):
        self.x = x
        self.y = y
        if speed == "rand":
            self.speed = random.randint(10, 15) / 10
        else:
            self.speed = speed / 10  # speed∈Z, 10<=speed<=15, speed值越小移动越快
        if likemoving == "rand":
            self.likemoving = True if random.randint(1, 2) == 1 else False
        else:
            self.likemoving = likemoving

    def left(self):
        if self.x == 0:
            return
        if Map[self.y][self.x - 1].strID != "air":
            return
        if iTick < self.iLastLeftTick + iMovingWaitTime * self.speed:
            return
        self.x = self.x - 1
        self.iLastLeftTick = iTick
        self.strLastMoveType = "l"

    def right(self):
        if self.x == 1023:
            return
        if Map[self.y][self.x + 1].strID != "air":
            return
        if iTick < self.iLastRightTick + iMovingWaitTime * self.speed:
            return
        self.x = self.x + 1
        self.iLastRightTick = iTick
        self.strLastMoveType = "r"

    def jump(self):
        if Map[self.y + 1][self.x].strID != "air":
            return
        if Map[self.y - 1][self.x].strID == "air":
            return
        self.y = self.y + 1
        self.iLastJumpTick = iTick

    def fall(self):
        if Map[self.y - 1][self.x].strID != "air":
            return
        if iTick <= iMovingWaitTime * 5 + self.iLastJumpTick:
            return
        self.y = self.y - 1

    def do_nothing(self):  # 空闲时走动
        self.fall()
        if iTick % 10 == 0:
            if random.randint(1, (5 if self.likemoving else 10)) == 1:
                if self.strLastMoveType == "n":
                    if random.randint(1, 2) == 1:
                        if self.x == 0:
                            return
                        if Map[self.y][self.x - 1].strID != "air":
                            self.jump()
                        self.left()
                    else:
                        if self.x == 1023:
                            return
                        if Map[self.y][self.x + 1].strID != "air":
                            self.jump()
                        self.right()
                elif self.strLastMoveType == "l":
                    if random.randint(1, 3) != 1:
                        if self.x == 0:
                            return
                        if Map[self.y][self.x - 1].strID != "air":
                            self.jump()
                        self.left()
                    else:
                        if self.x == 1023:
                            return
                        if Map[self.y][self.x + 1].strID != "air":
                            self.jump()
                        self.right()
                else:
                    if random.randint(1, 3) != 1:
                        if self.x == 0:
                            return
                        if Map[self.y][self.x - 1].strID != "air":
                            self.jump()
                        self.left()
                    else:
                        if self.x == 1023:
                            return
                        if Map[self.y][self.x + 1].strID != "air":
                            self.jump()
                        self.right()

    def to_dict(self):
        newdict = {"type": "chicken", "x": self.x, "y": self.y,
                   "speed": self.speed, "likemoving": self.likemoving}
        return newdict


# Class Converter


def dict_to_block(olddict):
    return Block(olddict["strID"], olddict["strBlocksShortName"], olddict["isCanBeDigged"],
                 olddict["strDiggingType"], olddict["iHardness"], olddict["GiveAfterDigging"],
                 olddict["isCanMake"], olddict["HowToMake"], olddict["strDigLevel"], olddict["color"])


def dict_to_item(olddict):
    if olddict["isNormal"]:
        return NormalItem(olddict["strID"], olddict["strName"], olddict["strMakeType"], olddict["strDigType"],
                          olddict["iAttack"])
    else:
        return None


def dict_to_class(olddict):
    if olddict["type"] == "chicken":
        return MobChicken(x=olddict["x"], y=olddict["y"], speed=olddict["speed"], likemoving=olddict["likemoving"])
    if olddict["type"] == "pig":
        return MobPig(x=olddict["x"], y=olddict["y"], speed=olddict["speed"], likemoving=olddict["likemoving"])


def dict_to_inventorything(olddict):
    typ = olddict["strType"]
    cnt = olddict["iCount"]
    dat = 0
    if typ == "item":
        dat = dict_to_item(olddict["data"])
    if typ == "block":
        dat = dict_to_block(olddict["data"])
    return InventoryThing(typ, dat, cnt)


# values
dSpeed: dict = {"wood": 2, "stone": 3, "iron": 5, "diamond": 6, "gold": 8}
DiggingLevelCmp: list = ["hand", "wood", "gold", "stone", "iron", "diamond"]
defaultBackgroundColor: pygame.color.Color = pygame.color.Color(255, 255, 255)
defaultWordColor: pygame.color.Color = pygame.color.Color(0, 0, 0)
diggingPos = (0, 0)
iDefaultEarthHigh: int = 32
iDefaultFontSize: int = 30
iDiggingBeginTick: int = -1
iFallingSpeed: float = 0.1
iHigh: int = 256
iHP: int = 20
iInHandThingNum: int = 0
iInventoryPageNum: int = 0
iLastJumpTick: int = -100
iLastLeftTick: int = -100
iLastRightTick: int = -100
iLFT: int = 0
iMaxTps: int = 30
iMovingWaitTime: int = 10
iRandomTickSpeed: float = 1
iRealTps: float = iMaxTps
iLastTpsTestSecond: float = 0
isAprilFool: bool = False
isDebugging: bool = True
isLeftMoving: bool = False
isRightMoving: bool = False
isPlayerDigging: bool = False
iTick: int = 0
LeftChunk: list = [[Block]*16]*iHigh
MainChunk: list = [[Block]*16]*iHigh
moblist: dict = {"chicken": MobChicken, "pig": MobPig}
Mobs = []
RightChunk: list = [[Block]*16]*iHigh
strGameMode = "no-creative"
windowSize = iLsize, iHsize = (800, 600)
x, y = 0, 0
EveryTickDoing = []
EveryRandomTickDoing = []

with open("blocksconfig.json", "r", encoding="UTF-8") as f:
    dTemp = eval(f.read().replace("true", "True").replace("false", "False"))
Blocks = {}
for i in dTemp:
    Blocks[i] = dict_to_block(dTemp[i])
ItemList = {
    "wooden_axe": NormalItem("wooden_axe", "木斧", "wood", "wood", 4),
    "stone_axe": NormalItem("stone_axe", "石斧", "stone", "wood", 5),
    "iron_axe": NormalItem("iron_axe", "铁斧", "iron", "wood", 7),
    "golden_axe": NormalItem("golden_axe", "金斧", "gold", "wood", 5),
    "diamond_axe": NormalItem("diamond_axe", "钻石斧", "diamond", "wood", 9),
    "wooden_pickaxe": NormalItem("wooden_pickaxe", "木镐", "wood", "stone", 2),
    "stone_pickaxe": NormalItem("stone_pickaxe", "石镐", "stone", "iron", 3),
    "iron_pickaxe": NormalItem("iron_pickaxe", "铁镐", "iron", "diamond", 5),
    "golden_pickaxe": NormalItem("golden_pickaxe", "金镐", "gold", "stone", 3),
    "diamond_pickaxe": NormalItem("diamond_pickaxe", "钻石镐", "diamond", "diamond", 6)
}

pygame.init()
tps = pygame.time.Clock()
screen = pygame.display.set_mode((800, 600), pygame.RESIZABLE)
pygame.display.set_caption("Wordcraft")
icon = pygame.image.load("Wordcraft.ico")

pygame.display.set_icon(icon)
screen.fill(defaultBackgroundColor)
pygame.display.update()


def get_tps():
    global iMovingWaitTime, iRealTps, iLastTpsTestSecond
    if iTick % 5 == 0:
        itmp = time()
        itmpS = itmp - iLastTpsTestSecond
        iLastTpsTestSecond = itmp
        iRealTps = round(5 / itmpS, 1) if iTick != 0 else iMaxTps
        iMovingWaitTime = int(iRealTps * 0.5)


def check_player_move(keys):
    global isPlayerDigging
    oldx, oldy = x, y
    if keys[pygame.K_a]:
        playerleft()
    if keys[pygame.K_d]:
        playerright()
    if keys[pygame.K_SPACE]:
        playerjump()
    if (oldx, oldy) != (x, y):
        isPlayerDigging = False
    playerfall()


def get_cmd():
    str_cmd = input()
    exec(str_cmd)
    return


def random_tick():
    for i in EveryRandomTickDoing:
        i()
    randomtick_grass_dirt_convert()
    # summons(64 - len(Mobs))


def randomtick_grass_dirt_convert():
    global x, y
    for i in range(iHigh):
        for j in range(16):
            if MainChunk[i][j].strID == "grass_block":
                if MainChunk[i+1][j].strID != "air":
                    MainChunk[i][j] = Blocks["dirt"]
                else:
                    near = [(k, l) for k in range(
                        max(0, i-1), min(iHigh-1, i+2)) for l in [max(0, j-1), min(15, j+1)]]
                    for k, l in near:
                        if MainChunk[k][l].strID == "dirt" and MainChunk[k+1][l].strID == "air":
                            MainChunk[k][l] = Blocks["grass_block"]


def save_map():
    save_chunk(x_to_chkID(x), MainChunk)
    save_chunk(x_to_chkID(x+16), RightChunk)
    save_chunk(x_to_chkID(x-16), LeftChunk)
    with open("./_mapdata/playerdata.wc", "w", encoding="UTF-8") as f:
        writein = {"x": x, "y": y, "iHP": iHP,
                   "Inventory": [[j.to_dict() for j in i] for i in Inventory]}
        f.write(str(writein))


def show_word(text, pos, color=defaultWordColor, fontsize=iDefaultFontSize, italic=False):
    font = pygame.font.Font("YaHei Consolas Hybrid.ttf",
                            fontsize, italic=italic)
    txt = font.render(text, True, color, defaultBackgroundColor)
    screen.blit(txt, pos)


def show_block(txt, pos, color=defaultWordColor, fontsize=iDefaultFontSize, italic=False):
    if len(txt) == 1:
        show_word(txt, pos, color, fontsize - 1, italic=italic)
    elif len(txt) == 2:
        show_word(txt[0] + "　", pos, color, fontsize // 2 - 1, italic=italic)
        a, b = pos
        show_word("　" + txt[1], (a, b + fontsize // 2),
                  color, fontsize // 2 - 1, italic=italic)
    elif len(txt) == 3:
        a, b = pos
        show_word(txt[:2], pos, color, fontsize // 2 - 1, italic=italic)
        show_word(" " + txt[-1] + " ",
                  (a, b + fontsize // 2), color, fontsize // 2 - 1, italic=italic)


def show_map():
    screen.fill(defaultBackgroundColor)
    i = [16, y-2]
    j = [0, x-8]
    i1_to = min(y+16, iHigh)
    j1_to = x+8
    while i[1] < i1_to:
        j[1] = x-7
        j[0] = 0
        while j[1] < j1_to:
            if x//16*16+16 > j[1] >= x//16*16:  # 主区块
                tmp0: Block = MainChunk[i[1]][j[1] % 16]
                tmp: str = tmp0.strBlocksShortName
            if j[1] < x//16*16:  # 左侧区块
                tmp0: Block = LeftChunk[i[1]][j[1] % 16]
                tmp: str = tmp0.strBlocksShortName
            if j[1] >= x//16*16+16:  # 右侧区块
                tmp0: Block = RightChunk[i[1]][j[1] % 16]
                tmp: str = tmp0.strBlocksShortName
            if (j[1], i[1]) == (x, y):
                tmp = "家"
            if (j[1], i[1]) == (x, y+1):
                tmp = "玩"
            if tmp == "　":
                if (j[1], i[1]) == (x+1, y+1):
                    tmp = "Ｋ"
                if (j[1], i[1]) == (x+1, y):
                    tmp = "Ｌ"
                if (j[1], i[1]) == (x, y-1):
                    tmp = "Ｍ"
                if (j[1], i[1]) == (x-1, y):
                    tmp = "Ｎ"
                if (j[1], i[1]) == (x-1, y+1):
                    tmp = "Ｏ"
                if (j[1], i[1]) == (x, y+2):
                    tmp = "Ｐ"
                for k in Mobs:
                    if k.x <= j[1] < k.x+k.size[0] \
                            and k.y <= i[1] < k.y+k.size[1] \
                            and k.shape[i[1]-k.y][j[1]-k.x] != 0:
                        tmp = k.strShortName
            show_block(tmp, (j[0]*iDefaultFontSize, i[0] * iDefaultFontSize), fontsize=iDefaultFontSize,
                       color=(tmp0.color if tmp0.strBlocksShortName == tmp else defaultWordColor))
            j[0] = j[0]+1
            j[1] = j[1]+1
        i[0] = i[0]-1
        i[1] = i[1]+1
    show_word("position:"+str((x, y)), (0, 15), fontsize=15)
    show_word("TPS:"+str(iRealTps), (0, 0), fontsize=15)
    show_word("HP:"+str(iHP), (0, 30), fontsize=15)
    show_word("Gametick:"+str(iTick), (0, 45), fontsize=15)

    for i in range(9):
        tmp = Inventory[iInventoryPageNum][i]
        show_block(tmp.data.strBlocksShortName if tmp.strType == "block" else tmp.data.strName, (i*iDefaultFontSize, 18*iDefaultFontSize),
                   ((255, 0, 0) if i == iInHandThingNum else defaultWordColor if tmp.strType == "item" else tmp.data.color))
        if tmp.iCount != 1:
            show_word(str(tmp.iCount), (i*iDefaultFontSize +
                      20, 18*iDefaultFontSize+20), fontsize=10)


def get_map_highest_block(findx):
    # 获取地图上指定位置最高的非空气方块的高度和方块数据
    if x//16 <= findx < x//16+16:  # find in main chunk
        Map = MainChunk
    elif x//16-16 <= findx < x//16:  # find in left chunk
        Map = LeftChunk
    elif x//16+16 <= findx < x//16+32:  # find in right chunk
        Map = RightChunk
    else:
        Map = load_chunk(x_to_chkID(findx))
    for iTmpY in range(255, -1, -1):
        if Map[iTmpY][findx % 16].strID != "air":
            NewRet = (iTmpY, Map[iTmpY][findx % 16])
            break
    else:
        NewRet = (-1, "none")
    return NewRet


def check_block_be_digged():
    global isPlayerDigging
    if isPlayerDigging:
        Handthing = Inventory[iInventoryPageNum][iInHandThingNum]
        if strGameMode == "creative":
            if strDiggingChunk == 'm':
                MainChunk[diggingPos[1]][diggingPos[0] % 16] = Blocks["air"]
            elif strDiggingChunk == 'r':
                RightChunk[diggingPos[1]][0] = Blocks["air"]
            else:
                LeftChunk[diggingPos[1]][15] = Blocks["air"]
            isPlayerDigging = False
        else:
            if MainChunk[diggingPos[1]][diggingPos[0] % 16].isCanBeDigged:
                iPlusTick = iRealTps//dSpeed[Handthing.data.strMakeType] if (Handthing.strTyp == "item" and Map[diggingPos[1]][diggingPos[0]].strDigType == Handthing.data.strDigType) and DiggingLevelCmp.index(
                    Handthing.data.strMakeType) >= DiggingLevelCmp.index(Map[diggingPos[1] % 16][diggingPos[0]].strDigLevel) else 5*iRealTps
                if iTick >= iDiggingBeginTick + iPlusTick:
                    if strDiggingChunk == 'm':
                        MainChunk[diggingPos[1]][diggingPos[0] %
                                                 16] = Blocks["air"]
                    elif strDiggingChunk == 'l':
                        LeftChunk[diggingPos[1]][15] = Blocks["air"]
                    else:
                        RightChunk[diggingPos[1]][0] = Blocks["air"]


def load_chunk(iChkID):
    filename = "_mapdata/chk" + str(iChkID) + ".wc"
    print(os.path.isfile(filename))
    if os.path.isfile(filename):  # 存在区块文件就读取
        with open(filename, "r", encoding="UTF-8") as f:
            data = f.read()
            data = eval(data)
            return [[dict_to_block(j) for j in i] for i in data]
    else:  # 不存在就创建区块
        return create_chunk(iChkID)


def create_chunk(iChkID):
    NewChk = [[Blocks["air"] for j in range(16)] for i in range(iHigh)]
    with open("./_mapdata/map.wc") as f:
        data = f.read()
        data = eval(data)
    if data["type"] == "flat":
        NewChk[0] = [deepcopy(Blocks["bedrock"]) for i in range(16)]
        NewChk[1] = NewChk[2] = [deepcopy(Blocks["stone"]) for i in range(16)]
        NewChk[3] = [deepcopy(Blocks["grass_block"] for i in range(16))]
    else:
        with open("./_mapdata/map.wc") as f:
            filetext = eval(f.read())
            sed = filetext["seed"]
        random.seed(sed*iChkID)
        Tmp = [iDefaultEarthHigh + (random.randint(-4, 4)) for i in range(16)]
        TreeHigh = [0 for i in range(16)]
        isTurn = False
        if os.path.isfile("./_mapdata/chk"+str(x_to_chkID(chkID_to_leftedge(iChkID)-1))+".wc"):
            Tmp[0] = get_map_highest_block(chkID_to_leftedge(iChkID)-1)[0]-3
            isTurn = False
        elif os.path.isfile("./_mapdata/chk"+str(x_to_chkID(chkID_to_leftedge(iChkID)+16))+".wc"):
            Tmp[0] = get_map_highest_block(chkID_to_leftedge(iChkID)+16)[0]-3
            isTurn = True

        for i in range(1, 16):
            iTmpNum = random.randint(0, 10)
            if iTmpNum == 0:
                Tmp[i] = Tmp[i - 1] - 1
            elif iTmpNum == 10:
                Tmp[i] = Tmp[i - 1] + 1
            else:
                Tmp[i] = Tmp[i - 1]
            if Tmp[i] >= 240:
                Tmp[i] = 32
            if Tmp[i] <= 8:
                Tmp[i] = 32
            TreeHigh[i] = 5 if random.randint(
                1, 40) == 1 and 3 < i < 12 else 0
        for i in range(16):
            NewChk[0][i] = Blocks["bedrock"]
            for j in range(Tmp[i] + 1, Tmp[i] + 3):
                NewChk[j][i] = Blocks["dirt"]
            NewChk[Tmp[i] + 3][i] = Blocks["grass_block"]
            for j in range(1, Tmp[i] + 1):
                NewChk[j][i] = Blocks["stone"]
            for j in range(Tmp[i] + 4, Tmp[i] + 4 + TreeHigh[i]):
                NewChk[j][i] = Blocks["wood"]
            tmp = Tmp[i] + 3 + TreeHigh[i]
            if TreeHigh[i] != 0:
                NewChk[tmp + 1][i] = NewChk[tmp][i - 1] = NewChk[tmp][i + 1] = NewChk[tmp - 1][i -
                                                                                               2] = NewChk[tmp - 1][
                    i - 1] = NewChk[tmp - 1][i + 1] = NewChk[tmp - 1][i + 2] = deepcopy(Blocks["leaf"])
        if isTurn:
            NewChk = [i[::-1] for i in NewChk]
    save_chunk(iChkID, NewChk)
    return NewChk


def save_chunk(chkID, chkdata):
    filename = "./_mapdata/chk" + str(chkID) + ".wc"
    with open(filename, "w", encoding="UTF-8") as f:
        writein = [[j.to_dict() for j in i] for i in chkdata]
        f.write(str(writein))


def load_map():
    global Inventory, x, y, iHP, MainChunk, LeftChunk, RightChunk, strGameMode
    if os.path.isfile("./_mapdata/chk0.wc"):  # 判断是否已生成第0区块，即是否为旧地图
        with open("./_mapdata/map.wc", "r", encoding="UTF-8") as f:
            strGameMode = eval(f.read())["gamemode"]
        # 从playerdata.wc获取玩家数据
        with open("./_mapdata/playerdata.wc", "r", encoding="UTF-8") as f:
            filetext = f.read()
            filetext = eval(filetext)
            x = filetext["x"]
            y = filetext["y"]
            iHP = filetext["iHP"]
            Inventory = [[dict_to_inventorything(
                j) for j in i] for i in filetext["Inventory"]]
    else:  # 新地图
        x = 0
        y = iHigh - 1
        iHP = 20
        with open("./_mapdata/map.wc", "r", encoding="UTF-8") as f:
            strGameMode = eval(f.read())["gamemode"]
            if strGameMode == "creative":
                Temp = [InventoryThing("block", Blocks[i], 1) for i in Blocks] + [InventoryThing(
                    "item", ItemList[i], 1) for i in ItemList] + [InventoryThing("block", Blocks["air"], 1)] * 50
                Inventory = [Temp[i * 9:(i + 1) * 9] for i in range(5)]
            else:
                Inventory = [[InventoryThing("block", Blocks["air"], 1)
                              for i in range(5)] for i in range(5)]
    # 打开对应的区块文件
    MainChunk = load_chunk(x_to_chkID(x))
    RightChunk = load_chunk(x_to_chkID(x + 16))
    LeftChunk = load_chunk(x_to_chkID(x - 16))


def x_to_chkID(x):
    if x >= 0:
        return x // 16 * 2
    else:
        return -(x // 16) * 2 - 1


def chkID_to_leftedge(chkID):
    if chkID % 2 == 0:
        return int(chkID / 2 * 16)
    else:
        return int((chkID + 1) / 2 * (-16))


def cmd_tp(iToX, iToY):
    tp(iToX, iToY)


def geth(iX):
    return get_map_highest_block(iX)[0] + 1


# FIXING!!
# def summons(n):
#     cnt = 0
#     if n == 0:
#         return
#     for i in range(n):
#         x = random.randint(10, 1020)
#         hi = get_map_highest_block(x)
#         mobtype = moblist[random.randint(0, len(moblist)-1)]
#         if hi[1].strID in mobtype.summonon:
#             var.set("Mobs", var.get("Mobs")+[mobtype(x, hi[0]+1)])


def check_die():
    global Mobs
    n = []
    if len(Mobs) > 512:
        Mobs = Mobs[: 256]
    for i in Mobs:
        if i.iHP > 0:
            n.append(i)
    Mobs = n


def check_player_update_blocks(keys):
    global Map, iDiggingBeginTick, diggingPos, isPlayerDigging, Inventory, LeftChunk, RightChunk, MainChunk, strDiggingChunk
    TempThing = Inventory[iInventoryPageNum][iInHandThingNum]
    updatePos = (-1, -1)
    if keys[pygame.K_k]:
        updatePos = (1, 1)
    elif keys[pygame.K_l]:
        updatePos = (1, 0)
    elif keys[pygame.K_m]:
        updatePos = (0, -1)
    elif keys[pygame.K_n]:
        updatePos = (-1, 0)
    elif keys[pygame.K_o]:
        updatePos = (-1, 1)
    elif keys[pygame.K_p]:
        updatePos = (0, 2)
    if updatePos == (-1, -1):
        pass
    elif TempThing.strType == "block" and TempThing.data.strID != "air":   # 放置方块
        # 讨论区块内部/边界
        if x % 16 == 0 and updatePos[0] == -1:  # 应在左侧区块
            if LeftChunk[updatePos[1]+y][15].strID == "air":
                LeftChunk[updatePos[1]+y][15] = deepcopy(TempThing.data)
        elif x % 16 == 15 and updatePos[1] == 1:  # 应在右侧区块
            if RightChunk[updatePos[1]+y][0].strID == "air":
                RightChunk[updatePos[1]+y][0] = deepcopy(TempThing.data)
        else:  # 应在主区快
            if MainChunk[updatePos[1]+y][updatePos[0]+x % 16].strID == "air":
                MainChunk[updatePos[1]+y][updatePos[0]+x %
                                          16] = deepcopy(TempThing.data)
        if strGameMode != "creative":
            if TempThing.iCount == 1:
                Inventory[iInventoryPageNum][iInHandThingNum] = InventoryThing(
                    "block", Blocks["air"], 1)
            else:
                Inventory[iInventoryPageNum][iInHandThingNum].iCount -= 1
    else:  # 破坏
        isPlayerDigging = True
        diggingPos = (updatePos[0]+x, updatePos[1]+y)
        iDiggingBeginTick = iTick
        if x % 16 == 0 and updatePos[0] == -1:
            strDiggingChunk = 'l'
        elif x % 16 == 15 and updatePos[0] == 1:
            strDiggingChunk = 'r'
        else:
            strDiggingChunk = 'm'
        print(strDiggingChunk)


def playerleft():
    global iLastLeftTick, x, MainChunk, LeftChunk, RightChunk
    if x % 16 == 0:  # Chunk Edge
        if [LeftChunk[y][15].strID, LeftChunk[y+1][15].strID] == ["air"]*2 and iTick >= iLastLeftTick+iMovingWaitTime:
            x = x-1
            iLastLeftTick = iTick
            save_chunk(x_to_chkID(x+17), RightChunk)
            RightChunk = MainChunk
            MainChunk = LeftChunk
            LeftChunk = load_chunk(x_to_chkID(x-16))
    else:
        if [MainChunk[y][x % 16-1].strID, MainChunk[y+1][x % 16-1].strID] == ["air"]*2 and iTick >= iLastLeftTick+iMovingWaitTime:
            x = x-1
            iLastLeftTick = iTick


def playerright():
    global x, iLastRightTick, MainChunk, LeftChunk, RightChunk
    if x % 16 == 15:  # Chunk Edge
        if [RightChunk[y][0].strID, RightChunk[y+1][0].strID] == ["air"]*2 and iTick >= iLastRightTick+iMovingWaitTime:
            x = x+1
            iLastRightTick = iTick
            save_chunk(x_to_chkID(x-17), LeftChunk)
            LeftChunk = MainChunk
            MainChunk = RightChunk
            RightChunk = load_chunk(x_to_chkID(x+16))
    else:
        if [MainChunk[y][x % 16+1].strID, MainChunk[y+1][x % 16+1].strID] == ["air"]*2 and iTick >= iLastRightTick+iMovingWaitTime:
            x = x+1
            iLastRightTick = iTick


def playerjump():
    global y, iLastJumpTick
    if MainChunk[y + 2][x % 16].strID == "air" and MainChunk[y-1][x % 16].strID != "air":
        y = y + 1
        iLastJumpTick = iTick


def playerfall():
    global iLFT, iHP, iFallingSpeed, y
    if MainChunk[y - 1][x % 16].strID != "air" or iTick <= iLastJumpTick + 4.2 * iMovingWaitTime:
        iLFT = iTick
        iHP = iHP - (0 if strGameMode ==
                     "creative" else max(int(pow(iFallingSpeed, 2)), 0))
        iFallingSpeed = 0.1
        return
    a = 8 / pow(iRealTps, 2)
    fnt = int(-0.5 * a + sqrt(0.25 * pow(a, 2) + 4 *
                              iFallingSpeed) / 2 / iFallingSpeed) + 1
    if iTick == iLFT + fnt:
        y = y - 1
        iFallingSpeed = sqrt(2 * a + pow(iFallingSpeed, 2))
        iLFT = iTick


def tp(iToX, iToY):
    global x, y, MainChunk, LeftChunk, RightChunk
    # Save old chunks
    save_chunk(x_to_chkID(x), MainChunk)
    save_chunk(x_to_chkID(x + 16), RightChunk)
    save_chunk(x_to_chkID(x - 16), LeftChunk)

    # move
    x = iToX
    y = iToY

    # load new chunks
    MainChunk = load_chunk(x_to_chkID(iToX))
    LeftChunk = load_chunk(x_to_chkID(iToX - 16))
    RightChunk = load_chunk(x_to_chkID(iToX + 16))


def handthing_change_check(keys):
    global iInHandThingNum
    if keys[pygame.K_1]:
        iInHandThingNum = 0
    elif keys[pygame.K_2]:
        iInHandThingNum = 1
    elif keys[pygame.K_3]:
        iInHandThingNum = 2
    elif keys[pygame.K_4]:
        iInHandThingNum = 3
    elif keys[pygame.K_5]:
        iInHandThingNum = 4
    elif keys[pygame.K_6]:
        iInHandThingNum = 5
    elif keys[pygame.K_7]:
        iInHandThingNum = 6
    elif keys[pygame.K_8]:
        iInHandThingNum = 7
    elif keys[pygame.K_9]:
        iInHandThingNum = 8


def inventory_page_change_check(keys):
    global iInventoryPageNum, iInHandThingNum
    if keys[pygame.K_UP]:
        iInventoryPageNum = iInventoryPageNum - 1
    elif keys[pygame.K_DOWN]:
        iInventoryPageNum = iInventoryPageNum + 1
    else:
        iInventoryPageNum = iInventoryPageNum
    iInventoryPageNum = iInventoryPageNum % 4
    if iInventoryPageNum != iInventoryPageNum:
        iInHandThingNum = 0


def thing_throw_check(keys):
    global Inventory
    if keys[pygame.K_q]:
        Inventory[iInventoryPageNum][iInHandThingNum] = dict_to_inventorything(
            {"strType": "block", "data": Blocks["air"].to_dict()})


def main():
    global iTick, screen, iLsize, iHsize
    show_map()
    pygame.display.flip()
    for eve in pygame.event.get():
        if eve.type == pygame.QUIT:
            save_map()
            sys.exit()
        if eve.type == pygame.VIDEORESIZE:
            windowSize = iLsize, iHsize = (
                eve.size[0], eve.size[1])
            screen = pygame.display.set_mode(
                windowSize, pygame.RESIZABLE)
        if eve.type == pygame.KEYDOWN:
            keys = pygame.key.get_pressed()
            check_player_update_blocks(keys)
            handthing_change_check(keys)
            inventory_page_change_check(keys)
            if keys[pygame.K_t]:
                get_cmd()
            thing_throw_check(keys)
    for i in EveryTickDoing:
        i()
    reqdict = openapi.get_requests()
    for req in reqdict:
        if reqdict[req]["retype"] == openapi.WCREQ_PLAYER_INFORMATION_GET:
            retdict = {"x": x, "y": y, "iHP": iHP, "Inventory": [
                [j.to_dict() for j in i] for i in Inventory]}
            openapi.answer(req, retdict)
        elif reqdict[req]["retype"] == openapi.WCREQ_WORLD_GET:
            retdict = {"iMainChunkID": x_to_chkID(
                x), "MainChunk": MainChunk, "LeftChunk": LeftChunk, "RightChunk": RightChunk}
            openapi.answer(req, retdict)
        if req in openapi.get_waiting_functions():
            openapi.get_waiting_functions()[req]()
    openapi.reset()
    keys = pygame.key.get_pressed()
    check_block_be_digged()
    check_player_move(keys)
    check_die()
    get_tps()
    if iTick % 100 % (100 // iRandomTickSpeed) == 0:
        random_tick()
    for i in Mobs:
        i.do_nothing()
    tps.tick(iMaxTps)
    iTick += 1


def pausechunk(chkID):
    l, r = chkID_to_leftedge(chkID), chkID_to_leftedge(chkID)+16
    for i in Mobs:
        if l <= i.x < r:
            i.pause = True


def wakechunk(chkID):
    l, r = chkID_to_leftedge(chkID), chkID_to_leftedge(chkID)+16
    for i in Mobs:
        if l <= i.x < r:
            i.pause = False


def load_modules():
    global EveryTickDoing, EveryRandomTickDoing
    modulelist = []
    with open(".\modules\modules.json") as f:
        modulelist = eval(f.read())
    for i in modulelist:
        exec("from modules."+i+" import modmain")
        EveryTickDoing += eval('modmain.EveryTick')
        EveryRandomTickDoing += eval('modmain.EveryRandomTick')


print("正在加载地图")
load_map()
load_modules()
print("已启动游戏")
tp(x, min(y, get_map_highest_block(x)[0]+1))
while True:
    main()
